package com.miplus.ccrm.core.service.iou;

import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
import com.miplus.ccrm.common.EnumRCode;
import com.miplus.ccrm.common.JPubUtils;
import com.miplus.ccrm.common.ServiceException;
import com.miplus.ccrm.core.controller.vo.action.ICmdResVo;
import com.miplus.ccrm.core.controller.vo.iou.IPayPlanResVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import org.dom4j.*;

@Slf4j
@Service
public class IouCommand {
    @Value("${iou.command.url:}")
    public String commandUrl;
    @Value("${iou.command.userid:}")
    private String commandUsrID;
    @Value("${iou.command.username:}")
    private String commandUsrName;
    @Value("${iou.command.userorgno:}")
    private String commandOrgNo;
    @Value("${iou.command.channelno:}")
    private String commandChNo;
    @Value("${iou.command.bussdate:}")
    private String bussDate;


    public List<IPayPlanResVo> cmdBillPayplan(String billno)
    {
        String requestBody=buildPayplanXml(billno);
        try{
            log.info("cmdBillPayplan({}):{}",billno,requestBody);
            HttpResponse<String> xmlResponse = Unirest.post(commandUrl)
                    .header("Content-Type", "application/json;charset=UTF-8")
                    .body(requestBody)
                    .asString();

            List<IPayPlanResVo> payplanlist=new ArrayList();
            int status = xmlResponse.getStatus();
            if (status == 200) {
                String respbody=xmlResponse.getBody();
                log.info("cmdBillPayplan({}).Response:{}",billno,respbody);

                org.dom4j.Document doc = createXmlDoc(respbody);
                org.dom4j.Element root = doc.getRootElement();
                String retcode=getRetCode(root);
                if(JPubUtils.IsEqual(retcode,"0000")){
                    List<org.dom4j.Node> ll = root.selectNodes("body/lmPmShdList/lmPmShd");
                    if(ll!=null){
                        for(org.dom4j.Node p : ll){
                            IPayPlanResVo resVo=buildPayPlan(p,billno);
                            resVo.setRepayNum(ll.size());
                            payplanlist.add(resVo);
                        }
                    }
                    return payplanlist;
                }
                else{
                    String retmsg = getRetCause(root);
                    throw new ServiceException(EnumRCode.CmdFail.value,retmsg+"("+retcode+")");
                }
            }
            else{
                throw new ServiceException(EnumRCode.CmdFail.value,
                        String.format("%s(%d)",xmlResponse.getStatusText(),status));
            }
        }
        catch (Exception e)
        {
            log.warn("cmdBillPayplan.Err:{}",e);
            throw new ServiceException(EnumRCode.CmdFail.value,"Error:"+e.getMessage());
        }
    }

    private IPayPlanResVo buildPayPlan(org.dom4j.Node p,String billno)
    {
        IPayPlanResVo resVo=new IPayPlanResVo();
        resVo.setBillno(billno);
        resVo.setCurrentNum(JPubUtils.StringToInt(getNodeValue(p,"perdNo")));
        resVo.setRepayDate(getNodeValue(p,"dueDt"));

        String amt=getNodeValue(p,"psPrcpAmt");//本金
        BigDecimal preRepayAmt=JPubUtils.ToBigDecimal(amt);
        resVo.setPreRepayPrin(amt);
        amt=getNodeValue(p,"psNormIntAmt"); //正常利息
        resVo.setPreRepayInt(amt);
        preRepayAmt=preRepayAmt.add(JPubUtils.ToBigDecimal(amt));
        amt=getNodeValue(p,"psOdIntAmt"); //逾期利息
        resVo.setPreOverdueFee(amt);
        preRepayAmt=preRepayAmt.add(JPubUtils.ToBigDecimal(amt));
        amt=getNodeValue(p,"psCommOdInt"); //复利
        resVo.setPreCompoundInt(amt);
        preRepayAmt=preRepayAmt.add(JPubUtils.ToBigDecimal(amt));
        amt=getNodeValue(p,"psFeeAmt"); //费用金额
        resVo.setPreRepayFee(amt);
        preRepayAmt=preRepayAmt.add(JPubUtils.ToBigDecimal(amt));

        resVo.setPreRepayAmt(preRepayAmt.toString());

        amt=getNodeValue(p,"setlPrcp");//已还本金
        BigDecimal realRepayAmt=JPubUtils.ToBigDecimal(amt);
        resVo.setRealRepayPrin(amt);
        amt=getNodeValue(p,"setlNormInt"); //已还利息
        resVo.setRealRepayInt(amt);
        realRepayAmt=realRepayAmt.add(JPubUtils.ToBigDecimal(amt));
        amt=getNodeValue(p,"setlOdIntAmt"); //已还逾期利息
        resVo.setRealOverdueFee(amt);
        realRepayAmt=realRepayAmt.add(JPubUtils.ToBigDecimal(amt));
        amt=getNodeValue(p,"setlCommOdInt"); //已还复利
        resVo.setRealCompoundInt(amt);
        realRepayAmt=realRepayAmt.add(JPubUtils.ToBigDecimal(amt));
        amt=getNodeValue(p,"setlFeeAmt"); //已还费用金额
        resVo.setRealRepayFee(amt);
        realRepayAmt=realRepayAmt.add(JPubUtils.ToBigDecimal(amt));
        resVo.setRealRepayAmt(realRepayAmt.toString());

        //剩余本金,剩余的只有本金，其它利息之类的需要在新的周期产生
        resVo.setLeftRepayPrin(getNodeValue(p,"psRemPrcp"));

        resVo.setIntRate(getNodeValue(p,"intRate"));
        resVo.setOdIntRate(getNodeValue(p,"odIntRate"));
        resVo.setAcctFeeAmt(getNodeValue(p,"acctFeeAmt"));
        resVo.setSetlAcctFeeAmt(getNodeValue(p,"setlAcctFeeAmt"));
        resVo.setPenalFeeAmt(getNodeValue(p,"penalFeeAmt"));
        resVo.setSetlPenalFeeAmt(getNodeValue(p,"setlPenalFeeAmt"));
        resVo.setLateFeeAmt(getNodeValue(p,"lateFeeAmt"));
        resVo.setSetlAcctFeeAmt(getNodeValue(p,"setlLateFeeAmt"));
        resVo.setAdvanceFeeAmt(getNodeValue(p,"advanceFeeAmt"));
        resVo.setSetlAdvanceFeeAmt(getNodeValue(p,"setlAdvanceFeeAmt"));

        resVo.setEffectInt(getNodeValue(p,"psWvNmInt"));
        resVo.setEffectOdInt(getNodeValue(p,"psWvOdInt"));
        resVo.setEffectFee(getNodeValue(p,"psWvCommInt"));

        String psOdInd=getNodeValue(p,"psOdInd");
        String setlInd=getNodeValue(p,"setlInd");
        if(JPubUtils.IsEmpty(psOdInd)){
            resVo.setStatus("E");
            resVo.setStatusdsc("已结清");
        }
        else if(JPubUtils.IsEqual(psOdInd,"Y")){
            resVo.setStatus("D");
            resVo.setStatusdsc("逾期");
        }
        else if(JPubUtils.IsEqual(setlInd,"N")){
            resVo.setStatus("F");
            resVo.setStatusdsc("未结清");
        }
        else{
            resVo.setStatus("N");
            resVo.setStatusdsc("正常");
        }

        return resVo;
    }

    private String getRetCode(org.dom4j.Element root)
    {
        try {
            org.dom4j.Node p = root.selectSingleNode("head/ec");
            return p.getText();
        }
        catch (Exception e){
            log.warn("getRetCode.Fail:{}",e.getMessage());
            return "";
        }
    }

    private String getRetCause(org.dom4j.Element root)
    {
        try {
            org.dom4j.Node p = root.selectSingleNode("head/em");
            return p.getText();
        }
        catch (Exception e){
            log.warn("getRetCause.Fail:{}",e.getMessage());
            return "";
        }
    }

    private static org.dom4j.Document createXmlDoc(String xmlBody)
    {
        try {
            if(JPubUtils.IsEmpty(xmlBody)) {
                xmlBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<request>\n</request>";
            }
            org.dom4j.Document doc =DocumentHelper.parseText(xmlBody);
            return doc;
        }
        catch (Exception e){
            log.warn("createXmlDoc.Fail:{}",e.getMessage());
            return null;
        }
    }

    private String buildPayplanXml(String billno)
    {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String seqno= commandChNo+sdf.format(new Date());

        log.info("buildPayplanXml({}):seqno={}",billno,seqno);

        org.dom4j.Document doc=createXmlDoc("");
        org.dom4j.Element root=doc.getRootElement();
        org.dom4j.Element head=DocumentHelper.createElement("head");

        org.dom4j.Node p=DocumentHelper.createElement("serviceId");
        p.setText("CMIS100003");
        head.add(p);
        p=DocumentHelper.createElement("serno");
        p.setText(seqno);
        head.add(p);
        sdf = new SimpleDateFormat("yyyy-MM-dd");
        p=DocumentHelper.createElement("tradeDate");
        p.setText(sdf.format(new Date()));
        head.add(p);
        sdf = new SimpleDateFormat("HH:mm:ss");
        p=DocumentHelper.createElement("tradeTime");
        p.setText(sdf.format(new Date()));
        head.add(p);
        p=DocumentHelper.createElement("channelNo");
        p.setText(commandChNo);
        head.add(p);
        root.add(head);

        org.dom4j.Element body=DocumentHelper.createElement("body");
        p=DocumentHelper.createElement("genGlNo");
        body.add(p);
        p=DocumentHelper.createElement("loanNo");
        p.setText(billno);
        body.add(p);
        p=DocumentHelper.createElement("userId");
        p.setText(commandUsrID);
        body.add(p);
        p=DocumentHelper.createElement("userName");
        p.setText(commandUsrName);
        body.add(p);
        p=DocumentHelper.createElement("userOrgNo");
        p.setText(commandOrgNo);
        body.add(p);
        p=DocumentHelper.createElement("enqTyp");
        p.setText("ALL");
        body.add(p);

        root.add(body);
        return doc.asXML();
    }

    private String getNodeValue(org.dom4j.Node np,String kname)
    {
        try {
            org.dom4j.Node p = np.selectSingleNode(kname);
            return p.getText();
        }
        catch (Exception e){
            log.warn("getNodeValue({}).Fail:{}",kname,e.getMessage());
            return "";
        }
    }

    private String getSetlSeqnumber(String channelcode)
    {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        SimpleDateFormat sdf2 = new SimpleDateFormat("HHmmss");
        Random random=new Random();
        //还款流水号=编码规则：13 + 渠道号2位 + 日期 8位+ 序号7位
        return "13"+channelcode+sdf.format(new Date())+sdf2.format(new Date())+random.nextInt(9);
    }

    //渠道编码：channelcode=借据里的渠道编码
    public ICmdResVo cmdDeduction(String billno, String idnumber, String custname, BigDecimal amt, String channelcode)
    {
        try{
            String requestBody=buildDeductXml(billno,idnumber,custname,amt,channelcode);

            log.info("cmdDeduction({}):{} To {}",billno,requestBody,commandUrl);

            HttpResponse<String> xmlResponse = Unirest.post(commandUrl)
                    .header("Content-Type", "application/json;charset=UTF-8")
                    .body(requestBody)
                    .asString();

            ICmdResVo resVo=new ICmdResVo();
            resVo.setBillNo(billno);
            int status = xmlResponse.getStatus();
            if (status == 200) {
                String respbody=xmlResponse.getBody();
                log.info("cmdDeduction({}).Response:{}",billno,respbody);

                org.dom4j.Document doc = createXmlDoc(respbody);
                org.dom4j.Element root = doc.getRootElement();
                String retcode=getRetCode(root);
                if(JPubUtils.IsEqual(retcode,"0000")){
                    org.dom4j.Node bodynode = root.selectSingleNode("body");
                    resVo.setSeqNo(getNodeValue(bodynode,"setlSeq"));
                    resVo.setRespCode(getNodeValue(bodynode,"setlSts"));
                    return resVo;
                }
                else{
                    String retmsg = getRetCause(root);
                    throw new ServiceException(EnumRCode.CmdFail.value,retmsg+"("+retcode+")");
                }
            }
            else{
                throw new ServiceException(EnumRCode.CmdFail.value,
                        String.format("%s(%d)",xmlResponse.getStatusText(),status));
            }
        }
        catch (Exception e)
        {
            log.warn("cmdDeduction.Err:{}",e);
            throw new ServiceException(EnumRCode.CmdFail.value,"Error:"+e.getMessage());
        }
    }

    private String buildDeductXml(String billno,String idnumber,String custname,BigDecimal amt,String channelcode)
    {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String seqno= commandChNo+sdf.format(new Date());

        String setlseq=getSetlSeqnumber(channelcode);

        log.info("buildDeductXml({}):seqno={},setlseq={}",billno,seqno,setlseq);

        org.dom4j.Document doc=createXmlDoc("");
        org.dom4j.Element root=doc.getRootElement();
        org.dom4j.Element head=DocumentHelper.createElement("head");

        org.dom4j.Node p=DocumentHelper.createElement("serviceId");
        p.setText("CMISCL200007");
        head.add(p);
        p=DocumentHelper.createElement("serno");
        p.setText(seqno);
        head.add(p);
        sdf = new SimpleDateFormat("yyyy-MM-dd");
        p=DocumentHelper.createElement("tradeDate");
        if(JPubUtils.IsEmpty(bussDate)) {
            p.setText(sdf.format(new Date()));
        }
        else{//仅测试使用：这样设置业务时间是为了银行核心系统测试流水正确
            p.setText(bussDate);
        }
        head.add(p);
        sdf = new SimpleDateFormat("HH:mm:ss");
        p=DocumentHelper.createElement("tradeTime");
        p.setText(sdf.format(new Date()));
        head.add(p);
        p=DocumentHelper.createElement("channelNo");
        p.setText(commandChNo);
        head.add(p);
        root.add(head);

        org.dom4j.Element body=DocumentHelper.createElement("body");
        p=DocumentHelper.createElement("setlSeq");
        p.setText(setlseq);
        body.add(p);
        p=DocumentHelper.createElement("loanNo");
        p.setText(billno);
        body.add(p);
        p=DocumentHelper.createElement("custName");
        p.setText(custname);
        body.add(p);
        p=DocumentHelper.createElement("idType");
        p.setText("20");
        body.add(p);
        p=DocumentHelper.createElement("idNo");
        p.setText(idnumber);
        body.add(p);
        p=DocumentHelper.createElement("setlOrigin");
        p.setText(channelcode);
        body.add(p);
        p=DocumentHelper.createElement("setlApplyDt");
        if(JPubUtils.IsEmpty(bussDate)) {
            p.setText(sdf.format(new Date()));
        }
        else{//仅测试使用：这样设置业务时间是为了银行核心系统测试流水正确
            p.setText(bussDate);
        }
        body.add(p);
        p=DocumentHelper.createElement("repayMode");
        p.setText("03");
        body.add(p);
        p=DocumentHelper.createElement("setlRemPrcpPaym");
        p.setText(amt.toString());
        body.add(p);
        p=DocumentHelper.createElement("setlChannel");
        p.setText(commandChNo);
        body.add(p);
        p=DocumentHelper.createElement("setlTyp");
        p.setText("N");
        body.add(p);

        root.add(body);
        return doc.asXML();
    }

    public ICmdResVo refDeductStatus(String billno,String idnumber,String custname,String setlSeq)
    {
        try{
            String requestBody=buildDedRefXml(billno,idnumber,custname,setlSeq);

            log.info("refDeductStatus({}):{} To {}",billno,requestBody,commandUrl);

            HttpResponse<String> xmlResponse = Unirest.post(commandUrl)
                    .header("Content-Type", "application/json;charset=UTF-8")
                    .body(requestBody)
                    .asString();

            ICmdResVo resVo=new ICmdResVo();
            resVo.setBillNo(billno);
            int status = xmlResponse.getStatus();
            if (status == 200) {
                String respbody=xmlResponse.getBody();
                log.info("refDeductStatus({}).Response:{}",billno,respbody);

                org.dom4j.Document doc = createXmlDoc(respbody);
                org.dom4j.Element root = doc.getRootElement();
                String retcode=getRetCode(root);
                if(JPubUtils.IsEqual(retcode,"0000")){
                    org.dom4j.Node bodynode = root.selectSingleNode("body");
                    resVo.setSeqNo(getNodeValue(bodynode,"setlSeq"));
                    resVo.setRespCode(getNodeValue(bodynode,"setlSts"));
                    return resVo;
                }
                else{
                    String retmsg = getRetCause(root);
                    throw new ServiceException(EnumRCode.CmdFail.value,retmsg+"("+retcode+")");
                }
            }
            else{
                throw new ServiceException(EnumRCode.CmdFail.value,
                        String.format("%s(%d)",xmlResponse.getStatusText(),status));
            }
        }
        catch (Exception e)
        {
            log.warn("refDeductStatus.Err:{}",e);
            throw new ServiceException(EnumRCode.CmdFail.value,"Error:"+e.getMessage());
        }
    }

    private String buildDedRefXml(String billno,String idnumber,String custname,String setlseq)
    {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String seqno= commandChNo+sdf.format(new Date());

        log.info("buildDedRefXml({}):seqno={},setlseq={}",billno,seqno,setlseq);

        org.dom4j.Document doc=createXmlDoc("");
        org.dom4j.Element root=doc.getRootElement();
        org.dom4j.Element head=DocumentHelper.createElement("head");

        org.dom4j.Node p=DocumentHelper.createElement("serviceId");
        p.setText("CMIS29200008");
        head.add(p);
        p=DocumentHelper.createElement("serno");
        p.setText(seqno);
        head.add(p);
        sdf = new SimpleDateFormat("yyyy-MM-dd");
        p=DocumentHelper.createElement("tradeDate");
        if(JPubUtils.IsEmpty(bussDate)) {
            p.setText(sdf.format(new Date()));
        }
        else{//仅测试使用：这样设置业务时间是为了银行核心系统测试流水正确
            p.setText(bussDate);
        }
        head.add(p);
        sdf = new SimpleDateFormat("HH:mm:ss");
        p=DocumentHelper.createElement("tradeTime");
        p.setText(sdf.format(new Date()));
        head.add(p);
        p=DocumentHelper.createElement("channelNo");
        p.setText(commandChNo);
        head.add(p);
        root.add(head);

        org.dom4j.Element body=DocumentHelper.createElement("body");
        p=DocumentHelper.createElement("setlSeq");
        p.setText(setlseq);
        body.add(p);
        p=DocumentHelper.createElement("loanNo");
        p.setText(billno);
        body.add(p);
        p=DocumentHelper.createElement("custName");
        p.setText(custname);
        body.add(p);
        p=DocumentHelper.createElement("idType");
        p.setText("20");
        body.add(p);
        p=DocumentHelper.createElement("idNo");
        p.setText(idnumber);
        body.add(p);

        root.add(body);
        return doc.asXML();
    }

    public ICmdResVo refPayAmount(String loanContNo,String billno,String paymMode)
    {
        try{
            String requestBody=buildPayRefXml(loanContNo,billno,paymMode);

            log.info("refPayAmount({}):{} To {}",billno,requestBody,commandUrl);

            HttpResponse<String> xmlResponse = Unirest.post(commandUrl)
                    .header("Content-Type", "application/json;charset=UTF-8")
                    .body(requestBody)
                    .asString();

            ICmdResVo resVo=new ICmdResVo();
            resVo.setBillNo(billno);
            int status = xmlResponse.getStatus();
            if (status == 200) {
                String respbody=xmlResponse.getBody();
                log.info("refPayAmount({}).Response:{}",billno,respbody);

                org.dom4j.Document doc = createXmlDoc(respbody);
                org.dom4j.Element root = doc.getRootElement();
                String retcode=getRetCode(root);
                resVo.setRespCode(retcode);
                if(JPubUtils.IsEqual(retcode,"0000")){
                    org.dom4j.Node bodynode = root.selectSingleNode("body");
                    //拖欠本息金额=odPrcpAmt 就是总欠款
                    BigDecimal amt=JPubUtils.ToBigDecimal(getNodeValue(bodynode,"odPrcpAmt"));
                    resVo.setRepayAmt(amt);
                    resVo.setRespCode("success");
                    return resVo;
                }
                else{
                    String retmsg = getRetCause(root);
                    throw new ServiceException(EnumRCode.CmdFail.value,retmsg+"("+retcode+")");
                }
            }
            else{
                throw new ServiceException(EnumRCode.CmdFail.value,
                        String.format("%s(%d)",xmlResponse.getStatusText(),status));
            }
        }
        catch (Exception e)
        {
            log.warn("refPayAmount.Err:{}",e);
            throw new ServiceException(EnumRCode.CmdFail.value,"Error:"+e.getMessage());
        }
    }

    private String buildPayRefXml(String loanContNo,String billno,String paymMode)
    {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String seqno= commandChNo+sdf.format(new Date());

        log.info("buildPayRefXml({}):seqno={}",billno,seqno);

        org.dom4j.Document doc=createXmlDoc("");
        org.dom4j.Element root=doc.getRootElement();
        org.dom4j.Element head=DocumentHelper.createElement("head");

        org.dom4j.Node p=DocumentHelper.createElement("serviceId");
        p.setText("CMIS04100005");
        head.add(p);
        p=DocumentHelper.createElement("serno");
        p.setText(seqno);
        head.add(p);
        sdf = new SimpleDateFormat("yyyy-MM-dd");
        p=DocumentHelper.createElement("tradeDate");
        if(JPubUtils.IsEmpty(bussDate)) {
            p.setText(sdf.format(new Date()));
        }
        else{//仅测试使用：这样设置业务时间是为了银行核心系统测试流水正确
            p.setText(bussDate);
        }
        head.add(p);
        sdf = new SimpleDateFormat("HH:mm:ss");
        p=DocumentHelper.createElement("tradeTime");
        p.setText(sdf.format(new Date()));
        head.add(p);
        p=DocumentHelper.createElement("channelNo");
        p.setText(commandChNo);
        head.add(p);
        root.add(head);

        org.dom4j.Element body=DocumentHelper.createElement("body");
        p=DocumentHelper.createElement("loanContNo");
        p.setText(loanContNo);
        body.add(p);
        p=DocumentHelper.createElement("loanNo");
        p.setText(billno);
        body.add(p);
        p=DocumentHelper.createElement("paymMode");
        p.setText(paymMode);
        body.add(p);

        root.add(body);
        return doc.asXML();
    }
}
